| Count | U_Growth | U_Trend | S_Growth | S_Trend | TPU_Growth | TPU_Trend | V_Growth | V_Trend | |
|---|---|---|---|---|---|---|---|---|---|
| ALL | 25993 | 0.81 | 0.64 | 0.8 | 0.72 | ||||
| ACA:AUSD | 13703 | 0.8 | 0.61 | 0.77 | 0.72 | ||||
| AUSD:LCDOT | 6563 | 0.62 | 0.59 | 0.97 | 0.61 | ||||
| DOT:LCDOT | 5727 | 0.65 | 0.67 | 1.04 | 0.54 |
Last updated: 2022-04-09 20:07:07
Date range of data: 2022-03-27T00:00:48.479 to 2022-04-09T23:54:00.404.
Sources: * SubQuery Network Swaps: * Acala-Swap-Day-Data * Karura-Swap-Day-Data Loans: * Acala-Loan-Data * Karura-Loan-Data
---
title: "Acala / Karura Dashboards"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
social: menu
source_code: embed
params:
network: Acala
window: 14
---
```{css custom1, echo=FALSE}
.dataTables_scrollBody {
max-height: 100% !important;
}
```
```{r global, include=FALSE}
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE,
comment = "#>"
)
library(kableExtra)
library(formattable)
library(lubridate)
library(flexdashboard)
library(DT)
# Helper function to concat
`%+%` <- function(a, b) paste0(a, b)
fixToken <- function(x) {
x <- gsub('fa://0', 'RMRK', x)
x <- gsub('lc://13', 'LCDOT', x)
x <- gsub('sa://0', 'taiKSM', x)
x
}
tokens <- rbind(c("ACA", "Acala", 12),
c("AUSD","Acala Dollar", 12),
c("taiKSM","Taiga KSM", 12),
c("DOT","Polkadot", 10),
c("LCDOT","Liquid Crowdloan DOT", 10),
c("LDOT","Liquid DOT", 10),
c("RENBTC","Ren Protocol BTC", 8),
c("CASH","Compound CASH", 8),
c("KAR","Karura", 12),
c("KUSD","Karura Dollar", 12),
c("KSM","Kusama", 12),
c("LKSM","Liquid KSM", 12),
c("TAI","Taiga", 12),
c("BNC","Bifrost Asgard", 12),
c("VSKSM","Bifrost Voucher Slot KSM", 12),
c("PHA","Phala Native Token", 12),
c("KINT","Kintsugi Native Token", 12),
c("KBTC","Kintsugi Wrapped BTC", 8)) %>%
as.data.table %>%
setnames(c("Token","Name","decimals"))
# remotes::install_github("ropensci/ghql") # if package is not already installed
library(jsonlite)
library(data.table)
library(ghql)
x <- GraphqlClient$new()
window <- params$window
endpoint <- params$endpoint
network <- params$network
if (network == 'Acala') {
dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala-dex"
loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-loan-subql"
swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/acala-swap-day-data"
official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/acala"
} else {
dex_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura-dex"
loan_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-loan-subql"
swap_endpoint <- "https://api.subquery.network/sq/rogerjbos/karura-test"
official_endpoint <- "https://api.subquery.network/sq/AcalaNetwork/karura"
}
getSwaps <- function(endpoint, window) {
# endpoint <- official_endpoint; window <- 10
# make a client
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:1000) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query('dexActions', '
{
query {
dexActions (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}, type: {equalTo: "swap"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node { timestamp id accountId token0Id token1Id volumeUSD data
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries$dexActions) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query$dexActions$pageInfo$endCursor
res <- as.data.table(result$data$query$dexActions$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query$dexActions$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList)
setnames(res, old = names(res), new = gsub("node.", "", names(res)))
if (substr(max(res$timestamp), 12, 13) < 23) {
maxdate <- as.Date(max(res$timestamp))-1
} else {
maxdate <- as.Date(max(res$timestamp))
}
res <- res[timestamp <= maxdate]
res[, date := as.Date(timestamp)]
setorder(res, timestamp)
# Replace foreign assets
res[, token0Id := fixToken(token0Id)]
res[, token1Id := fixToken(token1Id)]
# Normalize pairs
res[, pair := paste0(token0Id %+% ":" %+% token1Id)]
res[token1Id < token0Id, pair := paste0(token1Id %+% ":" %+% token0Id)]
res[, exclude := token0Id == token1Id]
res
}
getDailyPoolsQuery <- function(endpoint, window) {
# endpoint <- dex_endpoint; window <- 40
# make a client
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:1000) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query('dailyPools', '
{
query {
dailyPools (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node {
timestamp token0 {id} token1 {id} feeRateUSD dailyTradeVolumeUSD totalTVL txCount
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries$dailyPools) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query$dailyPools$pageInfo$endCursor
res <- as.data.table(result$data$query$dailyPools$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query$dailyPools$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList)
setnames(res, old = names(res), new = gsub("node.", "", names(res)))
res[, date := as.Date(timestamp)]
setorder(res, date)
setnames(res,
c("token0.id","token1.id","dailyTradeVolumeUSD","totalTVL","feeRateUSD"),
c("token0Id", "token1Id", "volumeUSD", "tvlUSD", "feeUSD"))
# Replace foreign assets
res[, token0Id := fixToken(token0Id)]
res[, token1Id := fixToken(token1Id)]
# Normalize pairs
res[, pair := paste0(token0Id %+% ":" %+% token1Id)]
res[token1Id < token0Id, pair := paste0(token1Id %+% ":" %+% token0Id)]
res[, feeUSD := as.numeric(feeUSD) / 1e18]
res[, volumeUSD := as.numeric(volumeUSD) / 1e18]
res[, tvlUSD := as.numeric(tvlUSD) / 1e18]
res
}
liquidityQuery <- function(endpoint, window) {
method <- 'dexActions'; maxn <- 1000
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:maxn) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query(method, '
{
query {
' %+% method %+% ' (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}, type: {in: ["addLiquidity","removeLiquidity"]}} ' %+% cursorStr %+% ') {
totalCount
edges {
node { timestamp id nodeId accountId type token0Id token1Id token0Amount token1Amount volumeUSD
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries[[method]]) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query[[method]]$pageInfo$endCursor
res <- as.data.table(result$data$query[[method]]$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query[[method]]$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList) %>%
setnames(names(res), gsub("node.", "", names(res)))
res[, date := as.Date(timestamp)]
setorder(res, date)
# Replace foreign assets
res[, token0Id := fixToken(token0Id)]
res[, token1Id := fixToken(token1Id)]
# Normalize pairs
res[, pair := paste0(token0Id %+% ":" %+% token1Id)]
res[token1Id < token0Id, pair := paste0(token1Id %+% ":" %+% token0Id)]
res
}
getLoansCollateralParamsQuery <- function(endpoint) {
method <- 'collateralParams'; maxn <- 1000
cli <- GraphqlClient$new(url = endpoint)
qry <- Query$new()
qry$query(method, '
{
query {
collateralParams {
totalCount
nodes {
collateral {
id
} maximumTotalDebitValue interestRatePerSec liquidationRatio
liquidationPenalty requiredCollateralRatio
}
}
}
}')
result <- cli$exec(qry$queries[[method]]) %>%
fromJSON(flatten=TRUE)
res <- as.data.table(result$data$query[[method]]$nodes)
# Replace foreign assets
res[, collateral.id := fixToken(collateral.id)]
res <- merge(res, tokens, by.x='collateral.id', by.y='Token')
res[, adj := as.numeric(substr(as.character(1e20),1, as.numeric(decimals) + 1))]
res[, maximumTotalDebitValue := as.numeric(maximumTotalDebitValue) / as.numeric(adj)]
res[, liquidationRatio := as.numeric(liquidationRatio) / 1e18]
res[, liquidationPenalty := as.numeric(liquidationPenalty) / 1e18]
res[, requiredCollateralRatio := as.numeric(requiredCollateralRatio) / 1e18]
res[, APR := (as.numeric(interestRatePerSec) / 1e18 + 1) ** (365 * 86400) - 1]
res
}
getLoansDailyPositionsQuery <- function(endpoint, window) {
# endpoint <-loan_endpoint
method <- 'dailyPositions'; maxn <- 1000
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:maxn) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query(method, '
{
query {
' %+% method %+% ' (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node {
id owner {id} collateral {id} depositAmount debitAmount depositVolumeUSD debitVolumeUSD
depositChangedUSD debitChangedUSD debitExchangeRate timestamp txCount
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries[[method]]) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query[[method]]$pageInfo$endCursor
res <- as.data.table(result$data$query[[method]]$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query[[method]]$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList) %>%
setnames(names(res), gsub("node.", "", names(res)))
res[, Date := as.Date(timestamp)]
res[, collateral.id := fixToken(collateral.id)]
res <- merge(res, tokens, by.x='collateral.id', by.y='Token')
res[, adj := as.numeric(substr(as.character(1e20),1, as.numeric(decimals) + 1))]
res[, depositAmount := as.numeric(depositAmount) / adj]
res[, debitAmount := as.numeric(debitAmount) / adj]
res[, depositVolumeUSD := as.numeric(depositVolumeUSD) / 1e18]
res[, debitVolumeUSD := as.numeric(debitVolumeUSD) / 1e18]
res[, depositChangedUSD := as.numeric(depositChangedUSD) / 1e18]
res[, debitChangedUSD := as.numeric(debitChangedUSD) / 1e18]
res[, debitExchangeRate := as.numeric(debitExchangeRate) / 1e18]
res
}
getLoansDailyCollateralQuery <- function(endpoint, window) {
method <- 'dailyCollaterals'; maxn <- 1000
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:maxn) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query(method, '
{
query {
' %+% method %+% ' (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node {
id collateral {id} depositAmount debitAmount depositVolumeUSD debitVolumeUSD
depositChangedUSD debitChangedUSD debitExchangeRate timestamp txCount
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries[[method]]) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query[[method]]$pageInfo$endCursor
res <- as.data.table(result$data$query[[method]]$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query[[method]]$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList) %>%
setnames(names(res), gsub("node.", "", names(res)))
res[, Date := as.Date(timestamp)]
res[, collateral.id := fixToken(collateral.id)]
res <- merge(res, tokens, by.x='collateral.id', by.y='Token')
res[, adj := as.numeric(substr(as.character(1e20),1, as.numeric(decimals) + 1))]
res[, depositAmount := as.numeric(depositAmount) / adj]
res[, debitAmount := as.numeric(debitAmount) / adj]
res[, depositVolumeUSD := as.numeric(depositVolumeUSD) / 1e18]
res[, debitVolumeUSD := as.numeric(debitVolumeUSD) / 1e18]
res[, depositChangedUSD := as.numeric(depositChangedUSD) / 1e18]
res[, debitChangedUSD := as.numeric(debitChangedUSD) / 1e18]
res[, debitExchangeRate := as.numeric(debitExchangeRate) / 1e18]
res
}
getSwapsDex <- function(endpoint, window) {
# endpoint <- dex_endpoint; window <- 10
# make a client
cli <- GraphqlClient$new(url = endpoint)
mindate <- today(tzone = 'UTC') - window
cursor <- ''
resList <- list()
for (i in 1:1000) {
if (cursor == '') {
cursorStr <- 'first:100'
} else {
cursorStr <- 'first:100 after:"' %+% cursor %+% '"'
}
qry <- Query$new()
qry$query('swaps', '
{
query {
swaps (filter: {timestamp: {greaterThanOrEqualTo: "' %+% mindate %+% '"}} ' %+% cursorStr %+% ') {
totalCount
edges {
node {
id address {
id
} pool {
id
} token0 {
id
} token1 {
id
} token0InAmount token1OutAmount
tradePath price0 price1 block {
id
} extrinsic {
id
} timestamp
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}
}
}')
result <- cli$exec(qry$queries$swaps) %>%
fromJSON(flatten=TRUE)
cursor <- result$data$query$swaps$pageInfo$endCursor
res <- as.data.table(result$data$query$swaps$edges)
res[, cursor := NULL]
print(i %+% " " %+% nrow(res))
resList[[i]] <- res
if (result$data$query$swaps$pageInfo$hasNextPage == FALSE) break
}
res <- rbindlist(resList)
setnames(res, old = names(res), new = gsub("node.", "", names(res)))
if (substr(max(res$timestamp), 12, 13) < 23) {
maxdate <- as.Date(max(res$timestamp))-1
} else {
maxdate <- as.Date(max(res$timestamp))
}
res <- res[timestamp <= maxdate]
res[, date := as.Date(timestamp)]
setorder(res, timestamp)
# Replace foreign assets
res[, token0.id := fixToken(token0.id)]
res[, token1.id := fixToken(token1.id)]
# Normalize pairs
res[, pair := paste0(token0.id %+% ":" %+% token1.id)]
res[token1.id < token0.id, pair := paste0(token1.id %+% ":" %+% token0.id)]
res[, exclude := token0.id == token1.id]
res
}
# queries ####
dailyLoanCollateral <- getLoansDailyCollateralQuery(loan_endpoint, window)
dailyLoanPositions <- getLoansDailyPositionsQuery(loan_endpoint, window)
collaterParams <- getLoansCollateralParamsQuery(loan_endpoint)
liquidity <- liquidityQuery(swap_endpoint, window)
liquidity[, volumeUSD := as.numeric(volumeUSD) / 1e18]
liq <- liquidity[, .(date, pair, type, volumeUSD)]
liq2 <- liq[, .(.N, sum(volumeUSD)), by = .(pair, date, type)]
setnames(liq2, c("date","N","V2"), c("Date","Observations","volumeUSD"))
liq2[type == 'removeLiquidity', volumeUSD := -volumeUSD]
liq <- liq[, .(.N, sum(volumeUSD)), by = .(date, type)]
setnames(liq, c("date","N","V2"), c("Date","Observations","volumeUSD"))
liq[type == 'removeLiquidity', volumeUSD := -volumeUSD]
# swaps2 <- getSwapsByDay(swap_endpoint, window)
swaps2 <- getDailyPoolsQuery(dex_endpoint, window)
swaps <- getSwapsDex(dex_endpoint, window)
# swaps <- getSwaps(official_endpoint, window)
# swaps[, volumeUSDFloat := as.numeric(volumeUSD)]
# swaps[volumeUSDFloat < 0]
# swaps[volumeUSDFloat < 0, id]
swaps <- merge(swaps, tokens, by.x='token0.id', by.y='Token') %>% setnames("decimals","decimals0")
swaps[, Name := NULL]
swaps <- merge(swaps, tokens, by.x='token1.id', by.y='Token') %>% setnames("decimals","decimals1")
swaps[, Name := NULL]
swaps[, adj0 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals0) + 1))]
swaps[, adj1 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals1) + 1))]
swaps[, token0InAmount := as.numeric(token0InAmount)]
swaps[, token1OutAmount := as.numeric(token1OutAmount)]
swaps[, price0 := as.numeric(price0) / 1e18]
swaps[, price1 := as.numeric(price1) / 1e18]
swaps[, amount0 := token0InAmount / adj0]
swaps[, amount1 := token1OutAmount / adj1]
swaps[, token0.id := fixToken(token0.id)]
swaps[, token1.id := fixToken(token1.id)]
swaps[, tradePath := fixToken(tradePath)]
swaps[, pathLength := length(strsplit(tradePath, ",")[[1]]) - 1, by = id]
swaps[, volume0USD := amount0 * price0]
swaps[, volume1USD := amount1 * price1]
swaps[, volumeUSDFloat := (volume0USD + volume1USD) / 2]
swaps[volume0USD == 0 & volume1USD > 0, volumeUSDFloat := volume1USD]
swaps[volume1USD == 0 & volume0USD > 0, volumeUSDFloat := volume0USD]
mysort <- function(a, b) ifelse(a < b, a %+% ":" %+% b, b %+% ":" %+% a)
getPath <- function(tradePath) {
# tradePath <- swaps[1]$tradePath
tp <- strsplit(tradePath, ",")[[1]]
n <- length(tp) - 1
if (n == 3) {
return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), mysort(tp[3],tp[4])))
} else if (n == 2) {
return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), "NA:NA"))
}
list(mysort(tp[1],tp[2]), "NA:NA", "NA:NA")
}
swaps[, c("pair1", "pair2", "pair3") := getPath(tradePath), by = id]
swaps[, fee := volumeUSDFloat * .03]
swaps[, feeAdj := volumeUSDFloat * .03 * pathLength]
setnames(swaps, "address.id", "accountId")
# setnames(swaps, "AccountId", "accountId")
pairs <- rbind(swaps[exclude == FALSE, .N, by = pair1] %>% setnames("pair1", "Pair"),
swaps[exclude == FALSE, .N, by = pair2] %>% setnames("pair2", "Pair"),
swaps[exclude == FALSE, .N, by = pair3] %>% setnames("pair3", "Pair"))
pairs <- pairs[, sum(N), by = Pair]
# remove pairs with NA in them
pairs <- pairs[-grep("NA", pairs$Pair)]
pairs <- rbind(data.table(Pair = "ALL", V1 = sum(pairs$V1)), pairs)
pairs <- pairs[order(V1, decreasing = TRUE)] %>%
setnames(c("Pair", "Observations"))
tvl <- swaps2[date == max(date)]
stable_dex_pool_size <- tvl[grep("USD", tvl$pair), sum(abs(tvlUSD))]
# Calculate measures for each pair
user_status <- list()
trades_status <- list()
tpu_status <- list()
volume_status <- list()
users_list <- list()
trades_list <- list()
per_list <- list()
volume_list <- list()
# remove old params object before calling render with new params list
rm(params)
for (p in pairs$Pair) {
# p <- pairs$Pair[1]
try(rm(u_list, t_list, p_list, v_list), silent = TRUE)
outname <- "~/R_HOME/websites/web_acala/content/swap_" %+% network %+% "_" %+% p %+% ".html"
unlink(outname)
# Create report for each pair
rmarkdown::render("~/R_HOME/karura-reports/Swap_template.Rmd",
output_file = outname,
params = list(pair = p))
# Store the data for the table
user_status[p] <- activeUsersStatus
trades_status[p] <- tradesStatus
tpu_status[p] <- avgTradeStatus
volume_status[p] <- tradeVolumeStatus
users_list[[p]] <- u_list
trades_list[[p]] <- t_list
per_list[[p]] <- p_list
volume_list[[p]] <- v_list
}
d <- list()
for (x in pairs$Pair) {
d[x] <- paste0('', x, '', collapse = '')
}
inline_plot <- data.frame(Count = pairs$Observations,
U_Growth = unlist(user_status),
U_Trend = "",
S_Growth = unlist(trades_status),
S_Trend = "",
TPU_Growth = unlist(tpu_status),
TPU_Trend = "",
V_Growth = unlist(volume_status),
V_Trend = "")
row.names(inline_plot) <- unlist(d)
getSpecColor <- function(x) {
x[is.na(x)] <- 0
ifelse(x < 1, "red", "green")
}
inline_plot$U_Growth <- cell_spec(inline_plot$U_Growth, color = getSpecColor(inline_plot$U_Growth))
inline_plot$S_Growth <- cell_spec(inline_plot$S_Growth, color = getSpecColor(inline_plot$S_Growth))
inline_plot$TPU_Growth <- cell_spec(inline_plot$TPU_Growth, color = getSpecColor(inline_plot$TPU_Growth))
inline_plot$V_Growth <- cell_spec(inline_plot$V_Growth, color = getSpecColor(inline_plot$V_Growth))
p <- inline_plot %>%
kbl(booktabs = TRUE, escape = FALSE, align='rrrrrrrrr') %>%
add_header_above(c(" " = 1, " " = 1, "Active Users" = 2, "Swap Trades" = 2, "Trades Per User" = 2, "Trading Volume" = 2)) %>%
kable_paper(full_width = FALSE) %>%
column_spec(4, image = spec_plot(users_list, same_lim = FALSE)) %>%
column_spec(6, image = spec_plot(trades_list, same_lim = FALSE)) %>%
column_spec(8, image = spec_plot(per_list, same_lim = FALSE)) %>%
column_spec(10, image = spec_plot(volume_list, same_lim = FALSE))
```
### `r network` Swap Performance Summary
```{r plot_acala, result='asis', out.height = 12}
p
```
Last updated: `r Sys.time()`
Date range of data: `r min(swaps$timestamp)` to `r max(swaps$timestamp)`.
Sources:
* [SubQuery Network](https://explorer.subquery.network/)
Swaps:
* [Acala-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/acala-swap-day-data)
* [Karura-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/karura-swap-day-data)
Loans:
* [Acala-Loan-Data](https://api.subquery.network/sq/rogerjbos/acala-loan-subql)
* [Karura-Loan-Data](https://api.subquery.network/sq/rogerjbos/karura-loan-subql)